{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 图像入门"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 读取cv2.imread(文件名，标记)\n",
    "- .IMREAD_COLOR：读入彩色图像,任何图像的透明度都将被忽略。这是**默认标志**\n",
    "- cv2.IMREAD_GRAYSCALE：以灰度模式读入图像\n",
    "- cv.IMREAD_UNCHANGED :加载图像，包括alpha通道"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'D:\\\\OneDrive - business\\\\jupyter notebook\\\\Modules-Learn\\\\assets'"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pwd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(217, 720)"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "img = cv2.imread('lena.jpg', 0)\n",
    "img.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**警告**\n",
    "\n",
    "即使图像路径错误，它也不会抛出任何错误，但print img会给你None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 显示图像cv2.imshow\n",
    "- cv2.waitKey（）是一个键盘绑定函数。它的参数是以毫秒为单位的时间。该函数等待任何键盘事件的指定毫秒。如果您在该时间内按任意键，程序将继续。如果为**0，则无限期等待键击**。它也可以设置为检测特定的键击，如果按下键a等.\n",
    "- cv2.destroyAllWindows() 只是破坏了我们创建的所有窗口。如果要销毁任何特定窗口，请使用函数 **cv2.destroyWindow()**，其中传递确切的窗口名称作为参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cv2.imshow('image', img)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有一种特殊情况，您可以在以后创建窗口并将图像加载到该窗口。在这种情况下，您可以指定窗口**是否可调整大小**。它使用函数**cv.namedWindow()** 完成。**默认情况下，标志为cv.WINDOW_AUTOSIZE**。但是如果将flag指定为**cv.WINDOW_NORMAL**，则可以调整窗口大小。当图像尺寸过大并向窗口添加轨迹栏时，它会很有用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cv2.namedWindow('image', cv2.WINDOW_NORMAL)\n",
    "cv2.imshow('image', img)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 写入图像cv.imwrite()\n",
    "第一个参数是文件名，第二个参数是要保存的图像。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cv2.imwrite('messigray.png', img)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "练习加载一个灰度图，显示图片，按下‘s’键保存后退出，或者按下ESC键退出不保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "img = cv2.imread('45.jpg', 0)\n",
    "cv2.imshow('image', img)\n",
    "k = cv2.waitKey(0)\n",
    "if k == 27:\n",
    "    cv2.destroyAllWindows()  #wait for ESC key to exit\n",
    "elif k == ord('s'):\n",
    "    cv2.imwrite('46.png', img)  #wait for 's' key to save and exit\n",
    "    cv2.destoryAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**如果您使用的是64位计算机，则必须k = cv.waitKey(0)按如下方式修改行：k = cv.waitKey(0) & 0xFF**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matplotlib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "from matplotlib import pyplot as plt\n",
    "img = cv2.imread('45.jpg', 0)\n",
    "plt.imshow(img, cmap='gray', interpolation='bicubic')\n",
    "plt.xticks([]), plt.yticks([])  #to hide tick values on X and Y axis\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 视频入门"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 显示摄像头实时图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Can't receive frame (stream end?). Exiting ...\n"
     ]
    }
   ],
   "source": [
    "import cv2\n",
    "# cap = cv2.VideoCapture('rtsp://admin:admin777@10.86.77.14:554/h264/ch1/sub/av_stream')\n",
    "cap = cv2.VideoCapture('rtsp://10.86.22.75:8554/test') \n",
    "\n",
    "while (1):\n",
    "    ret, img = cap.read()\n",
    "    if not ret:\n",
    "        print(\"Can't receive frame (stream end?). Exiting ...\")\n",
    "        break\n",
    "    cv2.imshow(\"Image\", img)\n",
    "    if cv2.waitKey(1) & 0xFF == ord('q'):\n",
    "        break\n",
    "cap.release()  # 释放摄像头\n",
    "cv2.destroyAllWindows()  # 释放窗口资源"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 海康网络摄像头\n",
    "\n",
    "RTSP地址：rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream\n",
    "说明：\n",
    "- username: 用户名。例如admin。\n",
    "- password: 密码。例如12345。\n",
    "- ip: 为设备IP。例如 192.0.0.64。\n",
    "- port: 端口号默认为554，若为默认可不填写。\n",
    "- codec：有h264、MPEG-4、mpeg4这几种。\n",
    "- channel: 通道号，起始为1。例如通道1，则为ch1。\n",
    "- subtype: 码流类型，主码流为main，辅码流为sub。\n",
    "\n",
    "例如，请求海康摄像机通道1的主码流，Url如下\n",
    "\n",
    "- 主码流：\n",
    "rtsp://admin:12345@192.0.0.64:554/h264/ch1/main/av_stream\n",
    "- 子码流：\n",
    "rtsp://admin:12345@192.0.0.64/mpeg4/ch1/sub/av_stream\n",
    "\n",
    "开放地址：\n",
    "\n",
    "湖南卫视：rtmp://58.200.131.2:1935/livetv/hunantv\n",
    "\n",
    "广西卫视 ：rtmp://58.200.131.2:1935/livetv/gxtv\n",
    "\n",
    "广东卫视：rtmp://58.200.131.2:1935/livetv/gdtv\n",
    "\n",
    "东方卫视：rtmp://58.200.131.2:1935/livetv/dftv \n",
    "\n",
    "rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 其他厂商摄像头地址\n",
    "\n",
    "格式参考：\n",
    "\n",
    "https://www.jiangyu.org/port-and-rtsp-address-of-several-ipcams/\n",
    "\n",
    "## 示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "\n",
    "\n",
    "# url = 'rtsp://admin:admin777@10.86.77.12:554/h264/ch1/sub/av_stream'\n",
    "url = 'rtmp://218.38.152.31/klive/klive.stream'\n",
    "cap = cv2.VideoCapture(url)\n",
    "\n",
    "while (1):\n",
    "    ret, img = cap.read()\n",
    "    if not ret:\n",
    "        print(\"Can't receive frame (stream end?). Exiting ...\")\n",
    "        break\n",
    "    cv2.imshow(\"Image\", img)\n",
    "    if cv2.waitKey(1) & 0xFF == ord('q'):\n",
    "        break\n",
    "cap.release()  # 释放摄像头\n",
    "cv2.destroyAllWindows()  # 释放窗口资源"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(576, 704, 3)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "img.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 获取图像参数(0-18)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(704.0, 576.0, 25.0)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# cap = cv2.VideoCapture(1)\n",
    "cap.get(cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_HEIGHT), cap.get(cv2.CAP_PROP_FPS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cv2.CAP_PROP_FOURCC"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| param | define |\n",
    "| --- | --- |\n",
    "| cv2.VideoCapture.get(0) | 视频文件的当前位置（播放）以毫秒为单位 |\n",
    "| cv2.VideoCapture.get(1) | 基于以 0 开始的被捕获或解码的帧索引 |\n",
    "| cv2.VideoCapture.get(2) | 视频文件的相对位置（播放）：0 = 电影开始，1 = 影片的结尾。 |\n",
    "| cv2.VideoCapture.get(3) | 在视频流的帧的宽度 |\n",
    "| cv2.VideoCapture.get(4) | 在视频流的帧的高度 |\n",
    "| cv2.VideoCapture.get(5) | 帧速率 |\n",
    "| cv2.VideoCapture.get(6) | 编解码的FOURCC 4 字 - 字符代码 |\n",
    "| cv2.VideoCapture.get(7) | 视频文件中的帧数 |\n",
    "| cv2.VideoCapture.get(8) | 返回对象的格式 |\n",
    "| cv2.VideoCapture.get(9) | 返回后端特定的值，该值指示当前捕获模式 |\n",
    "| cv2.VideoCapture.get(10) | 图像的亮度 (仅适用于照相机) |\n",
    "| cv2.VideoCapture.get(11) | 图像的对比度 (仅适用于照相机) |\n",
    "| cv2.VideoCapture.get(12) | 图像的饱和度 (仅适用于照相机) |\n",
    "| cv2.VideoCapture.get(13) | 色调图像 (仅适用于照相机) |\n",
    "| cv2.VideoCapture.get(14) | 图像增益 (仅适用于照相机)（Gain 在摄影中表示白平衡提升） |\n",
    "| cv2.VideoCapture.get(15) | 曝光 (仅适用于照相机) |\n",
    "| cv2.VideoCapture.get(16) | 指示是否应将图像转换为 RGB 布尔标志 |\n",
    "| cv2.VideoCapture.get(17) | × 暂时不支持 |\n",
    "| cv2.VideoCapture.get(18) | 立体摄像机的矫正标注（目前只有 DC1394 v.2.x 后端支持这个功能） |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "heading_collapsed": true
   },
   "source": [
    "### 设置摄像头参数\n",
    "\n",
    "前请记住设置之前的参数，否则影响以后正常使用\n",
    "\n",
    "```py\n",
    "ret = cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)\n",
    "ret = cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 播放视频文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "\n",
    "cap = cv2.VideoCapture('vtest.avi')\n",
    "while cap.isOpened():\n",
    "    ret, frame = cap.read()\n",
    "    # if frame is read correctly ret is True\n",
    "    if not ret:\n",
    "        print(\"Can't receive frame (stream end?). Exiting ...\")\n",
    "        break\n",
    "    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n",
    "    cv2.imshow('frame', gray)\n",
    "    if cv2.waitKey(1) == ord('q'):\n",
    "        break\n",
    "cap.release()\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 保存视频文件 [FourCC](http://www.fourcc.org/codecs.php)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cd C:\\Users\\Administrator\\Desktop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "cap = cv2.VideoCapture(0)\n",
    "# Define the codec and create VideoWriter object\n",
    "fourcc = cv2.VideoWriter_fourcc(*'XVID')\n",
    "out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))\n",
    "while cap.isOpened():\n",
    "    ret, frame = cap.read()\n",
    "    if not ret:\n",
    "        print(\"Can't receive frame (stream end?). Exiting ...\")\n",
    "        break\n",
    "    # 对称翻转（-1：上下翻转， 0：都翻转，1 左右翻转）\n",
    "    frame = cv2.flip(frame, 0)\n",
    "    # write the flipped frame\n",
    "    out.write(frame)\n",
    "    cv2.imshow('frame', frame)\n",
    "    if cv2.waitKey(1) == ord('q'):\n",
    "        break\n",
    "# Release everything if job is finished\n",
    "cap.release()\n",
    "out.release()\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 绘图函数\n",
    "涉及的函数：cv2.line() , cv2.circle() , cv2.rectangle() , cv2.ellipse() , cv2.putText()等\n",
    "需要设置的参数：\n",
    "- img 你想要绘制的图形的那副图像\n",
    "- color 形状的颜色，以RGB为例，需要传入的元组，例（255,0,0）代表蓝色，对于灰度图只需传入灰度值\n",
    "- thickness 线条的粗细，如果给一个闭合图形设置为-1，那么这个图形就会被填充，默认值为1\n",
    "- linetype 线条的类型，8连接，抗锯齿等。默认是8连接。cv2.LINE_AA为抗锯齿"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 画线\n",
    "需要告诉函数这条线的起点和终点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "# Create a black image\n",
    "img = np.zeros((512, 512, 3), np.uint8)\n",
    "\n",
    "#draw a diagonal blue line with thickness of 5 px\n",
    "cv2.line(img, (0, 0), (260, 260), (255, 0, 0), 5)\n",
    "\n",
    "#为了演示，建窗口显示出来\n",
    "cv2.namedWindow('image', cv2.WINDOW_NORMAL)\n",
    "cv2.resizeWindow('image', 1000, 1000)  #定义frame的大小\n",
    "cv2.imshow('image', img)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 画矩形（左边为整形）\n",
    "需要告诉函数左上角顶点和右下角顶点的坐标"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[245, 177, 110],\n",
       "       [ 80,  71,  54],\n",
       "       [ 16, 218, 214],\n",
       "       [212,   5, 108],\n",
       "       [ 31,  56, 149]], dtype=uint8)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.random.randint(0, 255, size=(5, 3),\n",
    "                           dtype=\"uint8\")\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "# Create a black image\n",
    "img = np.zeros((512, 512, 3), np.uint8)\n",
    "\n",
    "cv2.rectangle(img, (350, 0), (500, 128), a[0].tolist(), 3)\n",
    "\n",
    "cv2.namedWindow('image', cv2.WINDOW_NORMAL)\n",
    "cv2.resizeWindow('image', 1000, 1000)  #定义frame的大小\n",
    "cv2.imshow('image', img)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 画圆\n",
    "需要指定圆心坐标和半径大小，可以在上面矩形中画个圆"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(None, int)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "# Create a black image\n",
    "img = np.zeros((512, 512, 3), np.uint8)\n",
    "\n",
    "cv2.rectangle(img, (0, 0), (50, 50), (0, 255, 0), 3)  #矩形\n",
    "cv2.circle(img, (425, 63), 63, (0, 0, 255), -1)  #圆，-1为向内填充\n",
    "\n",
    "cv2.namedWindow('image', cv2.WINDOW_NORMAL)\n",
    "cv2.resizeWindow('image', 1000, 1000)  #定义frame的大小\n",
    "cv2.imshow('image', img)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 画椭圆\n",
    "一个参数是中心点的位置坐标。 下一个参数是和短的度。椭圆沿逆时方向旋的度。椭圆弧演 时方向始的度和结束度如果是 0 很 360就是整个椭圆。查看 cv2.ellipse() 可以得到更多信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "#Create a black image\n",
    "img = np.zeros((512, 512, 3), np.uint8)\n",
    "\n",
    "cv2.ellipse(img, (256, 256), (100, 50), 0, 0, 360, (0, 255, 0), -1)\n",
    "\n",
    "cv2.namedWindow('image', cv2.WINDOW_NORMAL)\n",
    "cv2.resizeWindow('image', 1000, 1000)  #定义frame的大小\n",
    "cv2.imshow('image', img)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 画多边形\n",
    "需要指定每个顶点的坐标，构建一个大小相等于行数X1X2的数组，行数就是点的数目，这个数组必须为int32。\n",
    "每条线会独立绘制。会比用 cv2.line() 一条一条的绘制快一些。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "#Create a black image\n",
    "img = np.zeros((512, 512, 3), np.uint8)\n",
    "\n",
    "pts = np.array([[10, 5], [20, 30], [70, 20], [50, 10]], np.int32)\n",
    "pts = pts.reshape((-1, 1, 2))\n",
    "#这里reshape的第一个参数为-1，表明这一维度的长度是根据后面的维度计算出来的\n",
    "cv2.polylines(img, [pts], True, (0, 255, 255))\n",
    "#注意第三个参数若是False，我们得到的是不闭合的线\n",
    "\n",
    "#为了演示，建窗口显示出来\n",
    "cv2.namedWindow('image', cv2.WINDOW_NORMAL)\n",
    "cv2.resizeWindow('image', 1000, 1000)  #定义frame的大小\n",
    "cv2.imshow('image', img)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 在图片上添加文字（坐标为整形）\n",
    "需要设置，文字内容，绘制的位置，字体类型、大小、颜色、粗细、类型等，这里推荐linetype=cv2.LINE_AA\n",
    "\n",
    "putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "# Create a black image\n",
    "img = np.zeros((512, 512, 3), np.uint8)\n",
    "\n",
    "font = cv2.FONT_HERSHEY_SIMPLEX\n",
    "cv2.putText(img, 'OpenCV', (10, 500), font, 0.75, (255, 255, 255), 2, cv2.LINE_AA)\n",
    "\n",
    "#为了演示，建窗口显示出来\n",
    "cv2.namedWindow('image', cv2.WINDOW_NORMAL)\n",
    "cv2.resizeWindow('image', 500, 500)  #定义frame的大小\n",
    "cv2.imshow('image', img)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "heading_collapsed": true
   },
   "source": [
    "# 处理鼠标事件(窗口）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "hidden": true
   },
   "source": [
    "## 回调函数\n",
    "当鼠标事件发生时就会被执行。\n",
    "比如左键按下，松开，左键双击等。可以通过鼠标事件获得相对应的图片上的坐标，根据这些信息可以做想做的事。\n",
    "\n",
    "所有鼠标事件回调函数都有一个统一的格式，不同的地方是被调用后的功能。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "hidden": true
   },
   "outputs": [],
   "source": [
    "from pprint import pprint\n",
    "#查看所有被支持的鼠标事件\n",
    "import cv2\n",
    "events = sorted([(i,cv2.__dict__[i]) for i in dir(cv2) if 'EVENT' in i], key=lambda x:x[1])\n",
    "pprint(events)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "hidden": true
   },
   "source": [
    "简单的程序，在图片上双击过的位置绘制一个圆圈"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "hidden": true,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "# mouse callback function\n",
    "def draw_circle(event, x, y, flags, param):\n",
    "    print(event, x, y, flags, param)\n",
    "    if event == cv2.EVENT_LBUTTONDBLCLK:\n",
    "        cv2.circle(img, (x, y), 100, (255, 0, 0), -1)\n",
    "\n",
    "\n",
    "# 创建图像与窗口并将窗口与回调函数绑定\n",
    "img = np.zeros((500, 500, 3), np.uint8)\n",
    "cv2.namedWindow('image')\n",
    "cv2.setMouseCallback('image', draw_circle)\n",
    "\n",
    "while (1):\n",
    "    cv2.imshow('image', img)\n",
    "    if cv2.waitKey(1) & 0xFF == ord('q'):  #按q键退出\n",
    "        break\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "hidden": true
   },
   "source": [
    "高级点程序\n",
    "根据我们选择的模式在拖动鼠标时绘制矩形。所以回调函数包含两个部分"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "hidden": true
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "#当鼠标按下时为True\n",
    "drawing = False\n",
    "#如果mode为true时绘制矩形，按下'm'变成绘制曲线\n",
    "mode = True\n",
    "ix, iy = -1, -1\n",
    "\n",
    "\n",
    "#创建回调函数\n",
    "def draw_circle(event, x, y, flags, param):\n",
    "    global ix, iy, drawing, mode\n",
    "    #当按下左键时返回起始位置坐标\n",
    "    if event == cv2.EVENT_LBUTTONDOWN:\n",
    "        drawing = True\n",
    "        ix, iy = x, y\n",
    "    #当左键按下并移动时绘制图形，event可以查看移动，flag查看是否按下\n",
    "    elif event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON:\n",
    "        if drawing == True:\n",
    "            if mode == True:\n",
    "                cv2.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)\n",
    "            else:\n",
    "                #绘制圆圈，小圆点连在一起就成了线，3代表笔画的粗细\n",
    "                cv2.circle(img, (x, y), 3, (0, 0, 255), -1)\n",
    "\n",
    "    #当鼠标松开时停止绘图\n",
    "    elif event == cv2.EVENT_LBUTTONUP:\n",
    "        drawing == False\n",
    "\n",
    "\n",
    "'''\n",
    "下面把回调函数与OpenCV窗口绑定在一起，在主循环中奖'm'键与模式转换绑定在一起\n",
    "'''\n",
    "img = np.zeros((500, 500, 3), np.uint8)\n",
    "cv2.namedWindow('image')\n",
    "cv2.setMouseCallback('image', draw_circle)\n",
    "while (1):\n",
    "    cv2.imshow('image', img)\n",
    "    k = cv2.waitKey(1)\n",
    "    if k & 0xFF == ord('m'):\n",
    "        mode = !mode\n",
    "    elif k & 0xFF == ord('q'):\n",
    "        break\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 用滑动条做调色板\n",
    "**使用函数**\n",
    "- cv2.getTrackbarPos() 创建滑动条\n",
    "- cv2.creatTrackbar()  获取对象的值\n",
    "\n",
    "通过调节滑动条来设定画板颜色。\n",
    "\n",
    "1.1窗口显示颜色，三个滑动条来设置RGB的颜色\n",
    "\n",
    "1.2当滑动滑动条时，窗口颜色实时发生改变，默认窗口为黑色。\n",
    "\n",
    "cv2.getTrackbarPos()参数详情：\n",
    "- 滑动条的名字\n",
    "- 滑动条被放置窗口的名字\n",
    "- 滑动条默认的位置\n",
    "- 滑动条最大的值\n",
    "- 回调函数，每次滑动都会调用回调函数，回调函数通常都会含有一个默认参数，就是滑动条的位置。\n",
    "\n",
    "滑动条的另一个应用就是用作**转换按钮**，默认OpenCV是不带有按钮函数的，这里以滑动条代替，需要先创建一个转换按钮，只有当转换按钮指向ON时滑动条才有用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "def nothing(x):\n",
    "    print(x)\n",
    "\n",
    "\n",
    "#创建一个黑色图像\n",
    "img = np.zeros((300, 512, 3), np.uint8)\n",
    "cv2.namedWindow('image')\n",
    "\n",
    "cv2.createTrackbar('R', 'image', 0, 255, nothing)\n",
    "cv2.createTrackbar('G', 'image', 0, 255, nothing)\n",
    "cv2.createTrackbar('B', 'image', 0, 255, nothing)\n",
    "\n",
    "switch = '0:OFF\\n1:ON'\n",
    "cv2.createTrackbar(switch, 'image', 0, 1, nothing)\n",
    "\n",
    "while (1):\n",
    "    cv2.imshow('image', img)\n",
    "    k = cv2.waitKey(1)\n",
    "    if k == ord('q'):  #按q键退出\n",
    "        break\n",
    "\n",
    "    r = cv2.getTrackbarPos('R', 'image')\n",
    "    g = cv2.getTrackbarPos('G', 'image')\n",
    "    b = cv2.getTrackbarPos('B', 'image')\n",
    "    s = cv2.getTrackbarPos(switch, 'image')\n",
    "\n",
    "    if s == 0:\n",
    "        img[:] = 0\n",
    "    else:\n",
    "        img[:] = [r, g, b]\n",
    "cv2.destroyAllWindows()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "236.52px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "position": {
    "height": "319.233px",
    "left": "745.795px",
    "right": "20px",
    "top": "80.9943px",
    "width": "467.358px"
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
